﻿/****************
* Authored by Zhuo "Wizcas" Chen
* 2018 © Wubi.fun all rights reserved
*****************/

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using Wubi.Utils;

public class Database
{
    const string RES_PATH = "Res";
    const string BLOCKS_PATH = "Blocks";
    const string PATTERN_PATH = "Patterns";
    const int START_LINE = 1;

    #region Singleton
    static Database _instance;
    public static Database instance { get { return _instance ?? (_instance = new Database()); } }
    #endregion

    Pattern _pattern = new Pattern();
    Dictionary<string, Res> _mapRes = new Dictionary<string, Res>();
    Dictionary<string, BlockData> _mapBlocks = new Dictionary<string, BlockData>();

    Database()
    {
        Reload();
    }

    #region Config
    void Reload()
    {
        _mapRes.Clear();
        _mapBlocks.Clear();
        _pattern = new Pattern();

        ReadRes();
        ReadBlocks();
        ReadPatterns();
    }

    void ReadFile(string path, System.Action<string[], int> readFunc)
    {
        if (readFunc == null) return;
        var file = Resources.Load<TextAsset>(path);
        if (file == null) return;
        using (System.IO.StringReader sr = new System.IO.StringReader(file.text))
        {
            var num = 0;
            string line;
            while ((line = sr.ReadLine()) != null)
            {
                readFunc(line.Split(','), num++);
            }
        }
    }

    void ReadRes()
    {
        ReadFile(RES_PATH, (values, num) =>
        {
            if (num < START_LINE) return;
            if (values.Length < 2 || string.IsNullOrEmpty(values[0])) return;
            _mapRes[values[0]] = new Res { id = values[0], name = values[1], score = int.Parse(values[2]) };
        });
    }

    void ReadBlocks()
    {
        ReadFile(BLOCKS_PATH, (values, num) =>
        {
            if (num < START_LINE) return;
            if (values.Length < 5 || string.IsNullOrEmpty(values[0])) return;
            var id = values[0];
            Color color;
            if (!ColorUtility.TryParseHtmlString(values[1], out color)) { color = Color.clear; }
            _mapBlocks[id] = new BlockData
            {
                id = id,
                color = color,
                income = ResAmount.Parse(values[2]),
                expense = ResAmount.Parse(values[3]),
                mergePrice = ResAmount.Parse(values[4])
            };
        });
    }

    void ReadPatterns()
    {
        ReadFile(PATTERN_PATH, (values, num) =>
        {
            if (num >= START_LINE) ParsePattern(values);
        });
    }

    void ParsePattern(string[] values)
    {
        for (var kind = Pattern.Kind.Vertical; kind < Pattern.Kind.COUNT; kind++)
        {
            _pattern.ParseCombo(kind, values[(int)kind - 1]);
        }
    }

    public override string ToString()
    {
        return string.Format(
            "===== Res =====\n{0}\n" +
            "===== Blocks =====\n{1}\n" +
            "===== Patterns =====\n{2}",
            _mapRes.Print("\n", kv => string.Format("{1} ({0})", kv.Key, kv.Value)),
            _mapBlocks.Print("\n"),
            _pattern);
    }

    #endregion
    #region Matching
    public IEnumerable<Combo> GetMatchingCombos(BlockStackMatrix matrix)
    {
        return _pattern.GetMatchingCombos(matrix);
    }
    #endregion

    #region API
    public Res[] resList { get { return _mapRes.Values.ToArray(); } }

    public Res GetRes(string resId)
    {
        if (string.IsNullOrEmpty(resId)) return Res.empty;

        Res res;
        if (!_mapRes.TryGetValue(resId, out res))
        {
            Debug.LogErrorFormat("Get Res error: invalid resId: {0}", resId);
            return Res.empty;
        }
        return res;
    }
    public BlockData GetBlockData(string blockId)
    {
        if (string.IsNullOrEmpty(blockId)) return null;
        BlockData blockData = null;
        if (!_mapBlocks.TryGetValue(blockId, out blockData))
        {
            Debug.LogErrorFormat("Get BlockData error: invalid blockId: {0}", blockId);
        }
        return blockData;
    }

    #endregion

}

#region BLOCK DATA
public class BlockData
{
    public string id;
    public Color color;
    public ResAmount income;
    public ResAmount expense;
    public ResAmount mergePrice;

    public override string ToString()
    {
        return string.Format("<color={1}>[{0}]</color> + {2} / - {3} / ↑ {4}", id, ColorUtility.ToHtmlStringRGB(color), income, expense, mergePrice);
    }
}
public struct ResAmount
{
    public string resId;
    public int baseAmount;
    public int fluctuate;
    public bool isEmpty { get { return string.IsNullOrEmpty(resId); } }
    public int tickAmount { get { return baseAmount + Mathf.RoundToInt((Random.value * 2 - 1) * fluctuate); } }

    public Res MakeRes(int amt = int.MinValue)
    {
        return Database.instance.GetRes(resId).MakeAmount(amt == int.MinValue ? tickAmount : amt);
    }

    public static ResAmount Parse(string str)
    {
        var parts = str.Split(new[] { ':' }, System.StringSplitOptions.RemoveEmptyEntries);
        if (parts == null || parts.Length < 2) return new ResAmount();
        return new ResAmount
        {
            resId = parts[0],
            baseAmount = int.Parse(parts[1]),
            fluctuate = (parts.Length < 3 || string.IsNullOrEmpty(parts[2])) ? 0 : int.Parse(parts[2])
        };
    }
    public override string ToString()
    {
        return string.Format("{0}: {1} (~{2})", resId, baseAmount, fluctuate);
    }
}
#endregion